#ifndef _THREADLIBFUNCTION_H
#define _THREADLIBFUNCTION_H

#if defined PLATFORM_WINDOWS
#include <windows.h>
#include <map>
#else
#include <pthread.h>
#include <unistd.h>
#endif

#include "threadexception.h"

namespace ThreadLib
{
typedef void (*ThreadFunc)(void*);
#if defined PLATFORM_WINDOWS
typedef DWORD ThreadID;
extern std::map< DWORD, HANDLE > g_handlemap;
#else
typedef pthread_t ThreadID;
#endif

class DummyData
{
public:
    ThreadFunc m_func;
    void* m_data;
};
#if defined PLATFORM_WINDOWS
inline DWORD WINAPI DummyRun( void* p_data )
#else
inline void* DummyRun( void* p_data )
#endif
{
    // convert the dummy data
    DummyData* data = (DummyData*)p_data;

    // run the function with the given data
    data->m_func( data->m_data );

    // now delete the data
    delete data;

    // and return 0.
    return 0;
}

inline ThreadID Create( ThreadFunc p_func, void* p_param )
{
    ThreadID t;
    DummyData* data = new DummyData;
    data->m_func = p_func;
    data->m_data = p_param;

#if defined PLATFORM_WINDOWS
    HANDLE h;
    h = CreateThread( NULL, 0, DummyRun, data, 0, (PDWORD)&t );
    if( h != 0 )
    {
        g_handlemap[t] = h;
    }
#else
    pthread_create( &t, 0, DummyRun, data );
#endif

    if( t == 0 )
    {
        delete data;
        throw Exception( CreationFailure );
    }
    return t;
}

inline ThreadID GetID()
{
#if defined PLATFORM_WINDOWS
    return GetCurrentThreadId();
#else
    return pthread_self();
#endif
}
inline void WaitForFinish( ThreadID p_thread )
{
#if defined PLATFORM_WINDOWS
    WaitForSingleObject( g_handlemap[p_thread], INFINITE );
    CloseHandle( g_handlemap[p_thread] );
    g_handlemap.erase( p_thread );
#else
    pthread_join( p_thread, NULL );
#endif
}

inline void Kill( ThreadID& p_thread )
{
#if defined PLATFORM_WINDOWS
    TerminateThread( g_handlemap[p_thread], 0 );
    CloseHandle( g_handlemap[p_thread] );
    g_handlemap.erase( p_thread );
#else
    pthread_cancel( p_thread );
#endif
}
inline void YieldThread( int p_milliseconds = 1 )
{
#if defined PLATFORM_WINDOWS
    Sleep( p_milliseconds );
#else
    usleep( p_milliseconds * 1000 );
#endif
}
}

#endif
